package com.yanqu.road.server.cross3server.conn;

import com.yanqu.road.NettyMgr;
import com.yanqu.road.initialize.YanQuCHInitializer;
import com.yanqu.road.connect.Connection;
import com.yanqu.road.connect.YanQuConnection;
import com.yanqu.road.connect.ServerConnectionGroup;
import com.yanqu.road.server.handle.CrossServerHandler;
import com.yanqu.road.message.YanQuMessage;
import com.yanqu.road.utils.net.ServerNetConfig;
import com.yanqu.road.utils.ServerType;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CrossServerConnectionGroup extends ServerConnectionGroup {

    private static Logger logger = LogManager.getLogger(CrossServerConnectionGroup.class.getName());

    protected InetSocketAddress addr;
    protected YanQuMessage firstMsg;
    Map<Integer, EventLoopGroupEntity> eventLoopGroupEntityMap;
    protected boolean rankCrossGroup;

    public CrossServerConnectionGroup(InetSocketAddress addr, YanQuMessage firstMsg, int size,
                                      ServerNetConfig serverNetConfig, int serverType) {
        super(serverNetConfig.getAddress(), serverNetConfig.getName(), size, serverNetConfig.getServerIndex(), serverNetConfig.getPort(), serverType);
        this.addr = addr;
        this.firstMsg = firstMsg;
        eventLoopGroupEntityMap = new ConcurrentHashMap<>();
    }

    public void setRankCrossGroup(boolean rankCrossGroup) {
        this.rankCrossGroup = rankCrossGroup;
    }

    public boolean isRankCrossGroup() {
        return rankCrossGroup;
    }

    public ChannelHandler createChannelHandler() {
        return new CrossServerHandler();
    }

    private Bootstrap initBootstrap(EventLoopGroup workerGroup) {
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(workerGroup);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.option(ChannelOption.TCP_NODELAY, true);
        bootstrap.option(ChannelOption.SO_RCVBUF, 1024 * 32);
        bootstrap.option(ChannelOption.SO_SNDBUF, 1024 * 32);
        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 300);
        bootstrap.handler(new YanQuCHInitializer(createChannelHandler()));
        return bootstrap;
    }

    @Override
    public YanQuConnection connect(int index) throws Exception {
        if (!eventLoopGroupEntityMap.containsKey(index)) {
            eventLoopGroupEntityMap.put(index, new EventLoopGroupEntity());
        }
        EventLoopGroupEntity entity = eventLoopGroupEntityMap.get(index);
        try {
            if (null != entity.getGroup() && null != entity.getFuture()) {
                if (entity.getFuture().channel().isActive()) {
                    entity.getFuture().channel().close();
                }
                entity.getGroup().shutdownGracefully();
            }
        } catch (Exception ex) {
            logger.error("connect error:", ex);
        }

        entity.setGroup(new NioEventLoopGroup(Runtime.getRuntime().availableProcessors()));

        EventLoopGroup workerGroup = entity.getGroup();

        Bootstrap bootstrap = initBootstrap(workerGroup);
        try {
            ChannelFuture future = bootstrap.connect(addr).syncUninterruptibly();
            entity.setFuture(future);
            if (future.isSuccess() && future.channel().isActive()) {
                YanQuConnection connection = this.createConnection(future.channel(), index, addr);
                connection.setConnectionId(index);
                connection.write(firstMsg);
                return connection;
            } else {
                logger.error("isSuccess : " + future.isSuccess() + ", isActive : " + future.channel().isActive());
            }
        } catch (Exception ex) {
            workerGroup.shutdownGracefully();
            throw ex;
        }

        return null;
    }

    @Override
    protected YanQuConnection createConnection(Channel channel, int index, InetSocketAddress addr) {
        YanQuConnection connection = new YanQuConnection(channel, true, 1024, index);
        allConnections[index] = connection;
        setEffective(true);
        return connection;
    }

    public void receiveHearBeat(int index) {
        Connection connection = allConnections[index];
        connection.setLastHeartTime(System.currentTimeMillis());
    }

    @Override
    public void keepAlive() {
        int size = 0;
        if(null != allConnections){
            size = allConnections.length;
        }
        long nowTime = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            synchronized (this) {
                Connection connection = allConnections[i];
                if (getServerType() == ServerType.SERVER_CROSS || getServerType() == ServerType.SERVER_CROSS_2 || getServerType() == ServerType.SERVER_CHANNEL || getServerType() == ServerType.BANQUET_CROSS) {
                    // 超过一分钟没收到消息
                    if (isConnectionAlive(connection) && nowTime < connection.getLastHeartTime() + 2 * 60 * 1000) {
                        continue;
                    }
                    if (null != connection) {
                        logger.error("KeepExpired----index: {}, nowTime: {}, checkTime: {}", i, nowTime, connection.getLastHeartTime());
                    }
                } else if (isConnectionAlive(connection)) {
                    continue;
                }

                if (tryConn(i)){
                    break;
                }
            }

        }
    }

    private boolean tryConn(int i) {
        try {
            YanQuConnection tempConn = connect(i);
            if (null == tempConn) {
                return true;
            }
            tempConn.setLastHeartTime(System.currentTimeMillis());
            allConnections[i] = tempConn;
            onReconnSucc(tempConn);
        } catch (Exception e) {
            logger.error("connection index={} addr={},error:" , i, addr, e);
            return true;
        }
        return false;
    }

    public void send(YanQuMessage packet) {
        write(packet.getPlayerId(), packet);
    }

    public void onReconnSucc(YanQuConnection connection) {
        int linkIndex = connection.getLinkIndex();
        Cross3ConnSet.sendRegisterServer(connection, linkIndex, getServerType());
        connection.getChannel().attr(NettyMgr.LINKED_CLIENT).set(this);
    }


    @Override
    public String toString() {
        return "[serverId : " + serverId + " , address : " + ipAddress + " , serverName : " + serverName +
                " , serverType : " + serverType + " , port : " + port + "]";
    }

}
